﻿using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using Button = System.Windows.Forms.Button;

namespace cmdrunner
{
    public partial class Form2 : Form
    {
        private readonly string FileName;
        private readonly int Index = -1;
        private readonly int CurrentPID = Process.GetCurrentProcess().Id;
        private readonly String CurrentPName = Process.GetCurrentProcess().ProcessName;

        private Process Process = null;

        private bool IsRunning = false;

        private bool IsClosed = false;

        private int ProcessId = -1;

        public Form2(string FileName) : this(FileName, -1, -1, -1)
        {

        }


        public Form2(string FileName, int Index, int Width, int Height)
        {
            InitializeComponent();
            this.FileName = FileName;
            this.Index = Index;

            if (Width > 0)
            {
                this.Width = Width;
            }
            if (Height > 0)
            {
                this.Height = Height;
            }

            if (Index != -1)
            {
                Text = "[" + Index + "] " + FileName;
            }
            else
            {
                Text = this.FileName;

            }

            FormClosing += Form2_FormClosing;
            FormClosed += Form2_FormClosed;

            label1.Text = Text;
            textBox.KeyDown += TextBox1_KeyDown;

        }

        private void Form2_FormClosed(object sender, FormClosedEventArgs e)
        {
            IsClosed = true;
        }

        private void Form2_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (!Stop())
            {
                e.Cancel = true;
            }
        }

        private void StartProcess()
        {

            Process p = new Process();
            ProcessStartInfo info = p.StartInfo;
            info.FileName = "cmd.exe";
            info.Arguments = "/C " + FileName;
            info.WorkingDirectory = Path.GetDirectoryName(this.FileName);

            info.UseShellExecute = false;
            info.CreateNoWindow = true;

            info.RedirectStandardOutput = true;
            info.RedirectStandardError = true;
            info.RedirectStandardInput = true;

            p.OutputDataReceived += p_OutputDataReceived;
            p.ErrorDataReceived += p_ErrorDataReceived;

            p.EnableRaisingEvents = true;
            p.Exited += p_Exited;

            try
            {
                p.Start();
            }
            catch (Exception ex)
            {
                SystemMessage("启动失败：\r\n" + ex.Message);

                Util.CloseProcess(p.Id, true);

                return;
            }

            Process = p;
            Process.BeginOutputReadLine();
            Process.BeginErrorReadLine();

            ProcessId = Process.Id;

        }
        private bool StopProcess()
        {
            try
            {
                if (Process != null && !Process.HasExited)
                {
                    Process.CancelErrorRead();
                    Process.CancelOutputRead();
                }
                Util.CloseProcess(ProcessId, true);
                SystemMessage("已成功终止本进程及其所有子级进程");
                return true;
            }
            catch (StopProcessFailException e)
            {
                SystemMessage("终止进程失败：\n" + e.Message);
                MessageBox.Show("终止进程失败：\n" + e.Message, Text);
                return false;
            }
        }

        public void Start()
        {
            if (IsRunning)
            {
                MessageBox.Show("正在运行，请先停止", Text);
                return;
            }

            StartProcess();

            IsRunning = true;
            button1.Enabled = false;
            button2.Enabled = true;
            SafeSetColor();
        }

        public bool Stop()
        {
            if (IsRunning)
            {
                if (!Util.ConfirmBox("正在运行，确定要停止？", Text))
                {
                    return false;
                }
            }
            else
            {
                return true;
            }

            if (!StopProcess())
            {
                return false;
            }

            IsRunning = false;
            button1.Enabled = true;
            button2.Enabled = false;
            SafeSetColor();

            return true;
        }

        private void p_Exited(object sender, EventArgs e)
        {

            //TODO 手动关闭窗口也会触发此事件，如何区分是自动终止还是手动终止？

            if (IsClosed || !IsRunning)
            {
                return;
            }
            SystemMessage("进程已自行终止");
            SystemMessage("尝试终止其所有子级进程");

            if (!StopProcess())
            {
                SystemMessage("终止进程失败");
                return;
            }

            IsRunning = false;
            SafeEnableBtn(button1, true);
            SafeEnableBtn(button2, false);
            SafeSetColor();
        }

        private void p_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (!String.IsNullOrEmpty(e.Data))
            {
                DisplayErrorMessage(e.Data);
            }
        }

        private void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (!String.IsNullOrEmpty(e.Data))
            {
                DisplayMessage(e.Data);
            }
        }

        private void SafeSetText(RichTextBox textBox, string text)
        {
            SafeSetText(textBox, text, null);
        }

        private void SafeSetText(RichTextBox textBox, string text, Color? color)
        {
            if (textBox.InvokeRequired)
            {
                Action safeWrite = delegate
                {
                    SafeSetText(textBox, text, color);
                };

                if (!textBox.IsDisposed)
                {
                    try
                    {
                        textBox.Invoke(safeWrite);
                    }
                    catch (Exception e)
                    {
                        
                    }
                }
            }
            else
            {
                if (!textBox.IsDisposed)
                {
                    try
                    {
                        if (color == null)
                        {
                            textBox.AppendText(text);
                            textBox.AppendText("\r\n");
                        }
                        else
                        {
                            textBox.SelectionStart = textBox.Text.Length;
                            textBox.SelectionLength = 0;
                            textBox.SelectionColor = (Color)color;
                            textBox.AppendText(text);
                            textBox.SelectionColor = textBox.ForeColor;
                            textBox.AppendText("\r\n");
                        }
                        textBox.ScrollToCaret();
                    }
                    catch (Exception e)
                    {
                        // FIXME 进程终止后，可能还会走到这里，但是元素已销毁，故报错
                        
                    }
                }
            }
        }

        private void SafeEnableBtn(Button button, bool enable)
        {
            if (button.InvokeRequired)
            {
                Action safeWrite = delegate
                {
                    SafeEnableBtn(button, enable);
                };
                if (!button.IsDisposed)
                {
                    button.Invoke(safeWrite);
                }
            }
            else
            {
                if (!button.IsDisposed)
                {
                    button.Enabled = enable;
                }
            }
        }

        private void SafeSetColor()
        {
            if (label1.InvokeRequired)
            {
                Action safeWrite = delegate
                {
                    SafeSetColor();
                };
                if (!label1.IsDisposed)
                {
                    label1.Invoke(safeWrite);
                }
            }
            else
            {
                if (!label1.IsDisposed)
                {
                    if (IsRunning)
                    {
                        label1.BackColor = Color.Teal;
                    }
                    else
                    {
                        label1.BackColor = Color.DarkGray;
                    }
                }
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Start();
        }
        private void button2_Click(object sender, EventArgs e)
        {
            Stop();
        }

        private void label1_Click(object sender, EventArgs e)
        {
            Process.Start("explorer.exe", Path.GetDirectoryName(this.FileName));
        }

        private void button3_Click(object sender, EventArgs e)
        {
            if (Stop())
            {
                Close();
            }
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {

        }

        private void TextBox1_KeyDown(object sender, KeyEventArgs e)
        {
            // TODO 
        }

        private void button4_Click(object sender, EventArgs e)
        {
            StringBuilder sb = new StringBuilder();

            MessageBox.Show(sb.ToString(), Text);
        }

        private void SystemMessage(String msg)
        {

            SafeSetText(textBox, "[" + CurrentPName + "系统][" + DateTime.Now.ToString("G") + "]：" + msg, Color.Gray);
        }

        private void DisplayMessage(String msg)
        {
            SafeSetText(textBox, msg);
        }

        private void DisplayErrorMessage(String msg)
        {
            SafeSetText(textBox, msg, Color.Red);
        }

        public bool Running()
        {
            return IsRunning;
        }
    }
}
